#ifndef THREADPOOL_H
#define THREADPOOL_H
#include <vector>
#include <queue>
#include <mutex>
#include <iostream>
#include <functional>
#include <condition_variable>
#include <memory>
#include <atomic>
#include <chrono>
#include <thread>
#include <unordered_map>
#include <future>
#include <cassert>

#define TASK_THRESHOLD 2
#define THREAD_THRESHOLD 20
#define THREAD_IDLETIME 10

using std::endl;
using std::cout;

using TaskFunc = std::function<void(int)>;
enum class ThreadPoolMode
{
    FIXED,
    CACHED
};

class Thread
{
public:
    Thread(const TaskFunc& taskFunc = nullptr): threadId_(generateId_++), taskFunc_(taskFunc){}
    void start()    //此时真正创建系统的线程
    {
        if(taskFunc_ == nullptr)
        {
            cout << "The taskFunc is invalid! " << endl;
            return;
        }
        
        std::thread t(taskFunc_, threadId_);
        t.detach();
    }

    int getId()
    {
        return threadId_;
    }

private:
    int threadId_;      
    //std::unique_ptr<std::thread> ptrThread_;      直接使用函数对象作为成员变量即可，系统线程在start方法中单独创建即可
    static int generateId_;
    TaskFunc taskFunc_;     
};

int Thread::generateId_ = 1;

class ThreadPool
{

    using Task = std::function<void()>;
public:
    ThreadPool()
    :mode_(ThreadPoolMode::FIXED),
    initialThreadCount_(4),
    threadThresHold_(THREAD_THRESHOLD),
    currentThreadCount_(0),
    taskThresHold_(TASK_THRESHOLD),
    currentTaskCount_(0),
    isPoolRunning_(false)
    {}
    
    ~ThreadPool()
    {
        while(currentTaskCount_ != 0);
        
        isPoolRunning_ = false;
        cout << "debug : ~ThreadPool start" << endl;

        std::unique_lock<std::mutex> lck(queueMutex_);
        notEmpty_.notify_all();
        exitCond_.wait(lck,[&](){return getCurrentThreadCount() == 0;});

        cout << "debug : ~ThreadPool end" << endl;
        
    }
    
    ThreadPool(const ThreadPool& pool) = delete;
    ThreadPool& operator=(const ThreadPool& pool) = delete;

    void close()
    {
        isPoolRunning_ = false;
    }
    int getCurrentThreadCount()
    {
        return currentThreadCount_;
    }

    void setMode(ThreadPoolMode mode)
    {
        if(isRunning())
        {
            cout << "Pool is Running!, please stop it first and try again" << endl;
            return;
        }
        mode_ = mode;
    }
    void setThreadThresHold(int threshold)
    {
        if(isRunning())
        {
            cout << "Pool is Running!, please stop it first and try again" << endl;
            return;
        }
        threadThresHold_ = threshold;
    }
    void setTaskThresHold(int threshold)
    {
        if(isRunning())
        {
            cout << "Pool is Running!, please stop it first and try again" << endl;
            return;
        }
        taskThresHold_ = threshold;
    }

    
    template<typename Func, typename... Args>
    auto submitTask(Func&& func, Args&&... args) -> std::future<decltype(func(args...))>   //由于参数声明在前面，故decltype的推导只能在后
    {
        //获取返回值(注意类型不能做变量)
        using returnType = decltype(func(args...));

        //使用package_task时，指明返回值即可，参数直接bind给函数对象
        //右值引用变量本身是一个左值，所以需要完美转发
        auto task = std::make_shared<std::packaged_task<returnType()>>
        (std::bind(std::forward<Func>(func), std::forward<Args>(args)...));

        std::future<returnType> result = task->get_future();

        std::unique_lock<std::mutex> lck(queueMutex_);
        //两种阻塞的情况：
        //1.其他线程太忙，任务无法得到有效处理 --> 通过同步，阻塞用户对结果的的获取；
        //2.任务数量到达阈值 --> 一定时间后若仍未获取到锁，则“降级”服务，直接返回；

        //任务提交失败时，返回该返回类型所对应的“零值”
        //返回前还需执行一下任务，否则可能会一直阻塞或直接dump掉
        while(currentTaskCount_ >= taskThresHold_)
        {
            cout << "debug : submitTask wait..." << endl;
            auto status = notFull_.wait_for(lck, std::chrono::seconds(1));
            if(status == std::cv_status::timeout)
            {
                //return std::future<returnType>(returnType());     future的构造函数不支持这么写
                cout << "debug : submitTask failed..." << endl;
                task = std::make_shared<std::packaged_task<returnType()>>
                ([](){return returnType();});
                
                (*task)();
                return task->get_future();
            }
        } 


        //将任务放入任务队列    lambda表达式作为中间层，线程拿到的任务看似执行的是lambda表达式，实则是执行其中的task
        taskQueue_.push([task](){(*task)();});

        currentTaskCount_++;
        notEmpty_.notify_all();

        if(mode_ == ThreadPoolMode::CACHED && currentThreadCount_ < threadThresHold_ && currentThreadCount_ < currentTaskCount_)
        {
            //创建线程
            createAndRunThread(1);      //?：新增线程数量的策略
        }        

        return result;
    }


    void start(int initialThreadCount)
    {
        isPoolRunning_ = true;
        if(initialThreadCount > threadThresHold_)       //超过阈值
        {
            //log
            return;
        }
        
        initialThreadCount_ = initialThreadCount;
        idleThreadCount_ = initialThreadCount;

        createAndRunThread(initialThreadCount);
    }

private:
    void createAndRunThread(int count)
    {
        for(int i = 0; i < count; i++)
        {
            Thread* t = new Thread(std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1));
            threads_.emplace(t->getId(), std::unique_ptr<Thread>(t));
            cout << "debug: created " << t->getId() << " thread" << endl;
            t->start();
        } 
        idleThreadCount_ += count;
        currentThreadCount_ += count;
    }

    void eraseThread(int threadId)
    {
        cout << "debug: ready to erase " << threadId << endl;
        if(threads_.count(threadId) == 1)
        {
            threads_.erase(threadId);
            currentThreadCount_--;
        }
        else
        {
            cout << "The thread has been erased!" << endl;
        }

        exitCond_.notify_all();     //每次删除一个线程后都唤醒一下主线程看是否可以析构了?
    }

    bool isRunning()
    {
        return isPoolRunning_;
    }

    void threadFunc(int threadId)   //任务无需作为参数传递，直接从队中获取即可
    {
        //thread_local int threadLocalId = threadId;
        while(isRunning())
        {
            //cout << "debug: " << "this is thread " << threadId << endl; 
            Task task;
            {
                std::unique_lock<std::mutex> lck(queueMutex_);
                while(currentTaskCount_ == 0)         //没有任务时对应两种情况：1.确实没来得及提交 2.线程池已退出，后续不会再提交
                {
                    cout << "debug: thread " << threadId << " wait for task..." << endl;
                    if(!isRunning())
                    {
                        eraseThread(threadId);
                        return;      //出线程函数后，线程自动销毁
                    }             

                    if(mode_ == ThreadPoolMode::CACHED)
                    {
                        auto status = notEmpty_.wait_for(lck, std::chrono::seconds(THREAD_IDLETIME));
                        //被唤醒且抢到锁了，就一定不会超时
                        if(status == std::cv_status::timeout && currentThreadCount_ > initialThreadCount_)
                        {
                            //回收空闲线程
                            eraseThread(threadId);
                            return;     //出线程函数后，线程自动销毁
                        }
                    }
                    else    //fixed模式下，只等待被唤醒即可
                    {
                        notEmpty_.wait(lck);
                    }
                }

                idleThreadCount_--;     //线程拿到任务就一定会去执行，所以空闲线程数量--
                task = taskQueue_.front();            //从任务队列中获取任务
                taskQueue_.pop();
                currentTaskCount_--;
                cout << "debug: thread " << threadId << " got the task..." << endl;
                //先通知
                if(currentTaskCount_ > 0)
                {
                    notEmpty_.notify_all();
                }
                notFull_.notify_all();
            }//再释放锁

            if(task != nullptr)
            {
                task();     //执行任务  
                cout << "debug: thread " << threadId << " finished the task..." << endl;                
            }

            idleThreadCount_++;
            //任务执行完后线程数量++，由于是atomic_int，所以无需担心线程安全
        }

        eraseThread(threadId);
    }

    ThreadPoolMode mode_;

    int initialThreadCount_;
    int threadThresHold_;       //线程数量阈值
    std::atomic_int idleThreadCount_;   //空闲线程数量
    std::atomic_int currentThreadCount_;     //当前线程数量
    std::unordered_map<int, std::unique_ptr<Thread>> threads_;

    std::queue<Task> taskQueue_;     
    int taskThresHold_;     //任务阈值
    std::atomic_int currentTaskCount_;     //不用.size()而用atomic_int，因为STL不是线程安全的，要保证线程安全
    
    std::mutex queueMutex_;
    std::condition_variable notFull_;
    std::condition_variable notEmpty_;
    std::condition_variable exitCond_;

    std::atomic_bool isPoolRunning_;
};


#endif